home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / gfx / x11 / x3270_3_2_16.lha / amiga_src / host.c < prev    next >
C/C++ Source or Header  |  2001-06-30  |  18KB  |  820 lines

  1. /*
  2.  * Copyright 1993, 1994, 1995, 1996, 1999, 2000 by Paul Mattes.
  3.  *  Permission to use, copy, modify, and distribute this software and its
  4.  *  documentation for any purpose and without fee is hereby granted,
  5.  *  provided that the above copyright notice appear in all copies and that
  6.  *  both that copyright notice and this permission notice appear in
  7.  *  supporting documentation.
  8.  */
  9.  
  10. /*
  11.  *    host.c
  12.  *        This module handles the ibm_hosts file, connecting to and
  13.  *        disconnecting from hosts, and state changes on the host
  14.  *        connection.
  15.  */
  16.  
  17. #include "globals.h"
  18. #include "appres.h"
  19. #include "resources.h"
  20.  
  21. #include "actionsc.h"
  22. #include "hostc.h"
  23. #include "macrosc.h"
  24. #include "menubarc.h"
  25. #include "popupsc.h"
  26. #include "telnetc.h"
  27. #include "trace_dsc.h"
  28. #include "utilc.h"
  29. #include "xioc.h"
  30.  
  31. #define RECONNECT_MS        2000    /* 2 sec before reconnecting to host */
  32. #define RECONNECT_ERR_MS    5000    /* 5 sec before reconnecting to host */
  33.  
  34. #define MAX_RECENT    5
  35.  
  36. enum cstate    cstate = NOT_CONNECTED;
  37. Boolean        std_ds_host = False;
  38. Boolean        non_tn3270e_host = False;
  39. Boolean        passthru_host = False;
  40. #define        LUNAME_SIZE    16
  41. char        luname[LUNAME_SIZE+1];
  42. char        *connected_lu = CN;
  43. char        *connected_type = CN;
  44. Boolean        ever_3270 = False;
  45.  
  46. char           *current_host = CN;
  47. char           *full_current_host = CN;
  48. unsigned short  current_port;
  49. #if defined(X3270_DISPLAY) /*[*/
  50. char           *reconnect_host = CN;
  51. #endif /*]*/
  52.  
  53. struct host *hosts = (struct host *)NULL;
  54. static struct host *last_host = (struct host *)NULL;
  55. static Boolean auto_reconnect_inprogress = False;
  56. static int net_sock = -1;
  57. static void save_recent(const char *);
  58.  
  59. #if defined(X3270_DISPLAY) /*[*/
  60. static void try_reconnect(void);
  61. #endif /*]*/
  62.  
  63. int host_getnetsock(void)
  64. {
  65. return(net_sock);
  66. }
  67.  
  68. static char *
  69. stoken(char **s)
  70. {
  71.     char *r;
  72.     char *ss = *s;
  73.  
  74.     if (!*ss)
  75.         return NULL;
  76.     r = ss;
  77.     while (*ss && *ss != ' ' && *ss != '\t')
  78.         ss++;
  79.     if (*ss) {
  80.         *ss++ = '\0';
  81.         while (*ss == ' ' || *ss == '\t')
  82.             ss++;
  83.     }
  84.     *s = ss;
  85.     return r;
  86. }
  87.  
  88.  
  89. /*
  90.  * Read the host file
  91.  */
  92. void
  93. hostfile_init(void)
  94. {
  95.     FILE *hf;
  96.     char buf[1024];
  97.     static Boolean hostfile_initted = False;
  98.     struct host *h;
  99.  
  100.     if (hostfile_initted)
  101.         return;
  102.     else
  103.         hostfile_initted = True;
  104.  
  105.     if (appres.hostsfile == CN)
  106.         appres.hostsfile = xs_buffer("%s/ibm_hosts", LIBX3270DIR);
  107.     hf = fopen(appres.hostsfile, "r");
  108.     if (hf != (FILE *)NULL) {
  109.         while (fgets(buf, sizeof(buf), hf)) {
  110.             char *s = buf;
  111.             char *name, *entry_type, *hostname;
  112.             char *slash;
  113.  
  114.             if (strlen(buf) > (unsigned)1 &&
  115.                 buf[strlen(buf) - 1] == '\n') {
  116.                 buf[strlen(buf) - 1] = '\0';
  117.             }
  118.             while (isspace(*s))
  119.                 s++;
  120.             if (!*s || *s == '#')
  121.                 continue;
  122.             name = stoken(&s);
  123.             entry_type = stoken(&s);
  124.             hostname = stoken(&s);
  125.             if (!name || !entry_type || !hostname) {
  126.                 xs_warning("Bad %s syntax, entry skipped",
  127.                     ResHostsFile);
  128.                 continue;
  129.             }
  130.             h = (struct host *)Malloc(sizeof(*h));
  131.             h->name = NewString(name);
  132.             h->hostname = NewString(hostname);
  133.  
  134.             /*
  135.              * Quick syntax extension to allow the hosts file to
  136.              * specify a port as host/port.
  137.              */
  138.             if ((slash = strchr(h->hostname, '/')))
  139.                 *slash = ':';
  140.  
  141.             if (!strcmp(entry_type, "primary"))
  142.                 h->entry_type = PRIMARY;
  143.             else
  144.                 h->entry_type = ALIAS;
  145.             if (*s)
  146.                 h->loginstring = NewString(s);
  147.             else
  148.                 h->loginstring = CN;
  149.             h->prev = last_host;
  150.             h->next = (struct host *)NULL;
  151.             if (last_host)
  152.                 last_host->next = h;
  153.             else
  154.                 hosts = h;
  155.             last_host = h;
  156.         }
  157.         (void) fclose(hf);
  158.     }
  159.  
  160.     /*
  161.      * Read the recent-connection file, and prepend it to the hosts list.
  162.      */
  163.     save_recent(CN);
  164. }
  165.  
  166. /*
  167.  * Look up a host in the list.  Turns aliases into real hostnames, and
  168.  * finds loginstrings.
  169.  */
  170. static int
  171. hostfile_lookup(const char *name, char **hostname, char **loginstring)
  172. {
  173.     struct host *h;
  174.  
  175.     hostfile_init();
  176.     for (h = hosts; h != (struct host *)NULL; h = h->next) {
  177.         if (h->entry_type == RECENT)
  178.             continue;
  179.         if (!strcmp(name, h->name)) {
  180.             *hostname = h->hostname;
  181.             *loginstring = h->loginstring;
  182.             return 1;
  183.         }
  184.     }
  185.     return 0;
  186. }
  187.  
  188. #if defined(LOCAL_PROCESS) /*[*/
  189. /* Recognize and translate "-e" options. */
  190. static const char *
  191. parse_localprocess(const char *s)
  192. {
  193.     int sl = strlen(OptLocalProcess);
  194.  
  195.     if (!strncmp(s, OptLocalProcess, sl)) {
  196.         if (s[sl] == ' ')
  197.             return(s + sl + 1);
  198.         else if (s[sl] == '\0') {
  199.             char *r;
  200.  
  201.             r = getenv("SHELL");
  202.             if (r != CN)
  203.                 return r;
  204.             else
  205.                 return "/bin/sh";
  206.         }
  207.     }
  208.     return CN;
  209. }
  210. #endif /*]*/
  211.  
  212. /*
  213.  * Strip qualifiers from a hostname.
  214.  * Returns the hostname part in a newly-malloc'd string.
  215.  */
  216. static char *
  217. split_host(char *s, Boolean *ansi, Boolean *std_ds, Boolean *passthru,
  218.     Boolean *non_e, char *xluname, char **port)
  219. {
  220.     *ansi = False;
  221.     *std_ds = False;
  222.     *passthru = False;
  223.     *non_e = False;
  224.     *xluname = '\0';
  225.     *port = CN;
  226.  
  227.     for (;;) {
  228.         char *at;
  229.  
  230.         if (!strncmp(s, "a:", 2) || !strncmp(s, "A:", 2)) {
  231.             *ansi = True;
  232.             s += 2;
  233.             continue;
  234.         }
  235.         if (!strncmp(s, "s:", 2) || !strncmp(s, "S:", 2)) {
  236.             *std_ds = True;
  237.             s += 2;
  238.             continue;
  239.         }
  240.         if (!strncmp(s, "p:", 2) || !strncmp(s, "P:", 2)) {
  241.             *passthru = True;
  242.             s += 2;
  243.             continue;
  244.         }
  245.         if (!strncmp(s, "n:", 2) || !strncmp(s, "N:", 2)) {
  246.             *non_e = True;
  247.             s += 2;
  248.             continue;
  249.         }
  250.         if ((at = strchr(s, '@')) != NULL) {
  251.             if (at != s) {
  252.                 int nc = at - s;
  253.         
  254.                 if (nc > LUNAME_SIZE)
  255.                     nc = LUNAME_SIZE;
  256.                 (void) strncpy(xluname, s, nc);
  257.                 xluname[nc] = '\0';
  258.             }
  259.             s = at + 1;
  260.             continue;
  261.         }
  262.  
  263.         break;
  264.     }
  265.     if (*s) {
  266.         char *r;
  267.         char *sep;
  268.  
  269.         r = NewString(s);
  270.         sep = strrchr(r, ':');
  271.         if (sep == NULL)
  272.             sep = strrchr(r, ' ');
  273.         else if (strrchr(r, ' ') != NULL) {
  274.             Free(r);
  275.             return CN;
  276.         }
  277.         if (sep != CN) {
  278.             *sep++ = '\0';
  279.             while (*sep == ' ')
  280.                 sep++;
  281.         }
  282.         if (port != (char **) NULL)
  283.             *port = sep;
  284.         return r;
  285.     } else
  286.         return CN;
  287. }
  288.  
  289.  
  290. /*
  291.  * Network connect/disconnect operations, combined with X input operations.
  292.  *
  293.  * Returns 0 for success, -1 for error.
  294.  * Sets 'reconnect_host', 'current_host' and 'full_current_host' as
  295.  * side-effects.
  296.  */
  297. int
  298. host_connect(const char *n)
  299. {
  300.     char nb[2048];        /* name buffer */
  301.     char *s;        /* temporary */
  302.     const char *chost;    /* to whom we will connect */
  303.     char *target_name;
  304.     char *ps = CN;
  305.     char *port = CN;
  306.     Boolean pending;
  307.     static Boolean ansi_host;
  308.     const char *localprocess_cmd = NULL;
  309.  
  310.     if (CONNECTED || auto_reconnect_inprogress)
  311.         return 0;
  312.  
  313.     /* Skip leading blanks. */
  314.     while (*n == ' ')
  315.         n++;
  316.     if (!*n) {
  317.         popup_an_error("Invalid (empty) hostname");
  318.         return -1;
  319.     }
  320.  
  321.     /* Save in a modifiable buffer. */
  322.     (void) strcpy(nb, n);
  323.  
  324.     /* Strip trailing blanks. */
  325.     s = nb + strlen(nb) - 1;
  326.     while (*s == ' ')
  327.         *s-- = '\0';
  328.  
  329. #if defined(X3270_DISPLAY) /*[*/
  330.     /* Remember this hostname, as the last hostname we connected to. */
  331.     if (reconnect_host != CN)
  332.         Free(reconnect_host);
  333.     reconnect_host = NewString(nb);
  334. #endif /*]*/
  335.  
  336.     /* Remember this hostname in the recent connection list and file. */
  337.     save_recent(nb);
  338.  
  339. #if defined(LOCAL_PROCESS) /*[*/
  340.     if ((localprocess_cmd = parse_localprocess(nb)) != CN) {
  341.         chost = localprocess_cmd;
  342.         port = appres.port;
  343.     } else
  344. #endif /*]*/
  345.     {
  346.         /* Strip off and remember leading qualifiers. */
  347.         if ((s = split_host(nb, &ansi_host, &std_ds_host,
  348.             &passthru_host, &non_tn3270e_host, luname, &port)) == CN)
  349.             return -1;
  350.  
  351.         /* Look up the name in the hosts file. */
  352.         if (hostfile_lookup(s, &target_name, &ps)) {
  353.             /*
  354.              * Rescan for qualifiers.
  355.              * Qualifiers, LU names, and ports are all overridden
  356.              * by the hosts file.
  357.              */
  358.             Free(s);
  359.             if (!(s = split_host(target_name, &ansi_host,
  360.                 &std_ds_host, &passthru_host, &non_tn3270e_host,
  361.                 luname, &port)))
  362.                 return -1;
  363.         }
  364.         chost = s;
  365.  
  366.         /* Default the port. */
  367.         if (port == CN)
  368.             port = appres.port;
  369.     }
  370.  
  371.     /*
  372.      * Store the original name in globals, even if we fail the connect
  373.      * later:
  374.      *  current_host is the hostname part, stripped of qualifiers, luname
  375.      *   and port number
  376.      *  full_current_host is the entire string, for use in reconnecting
  377.      */
  378.     if (n != full_current_host) {
  379.         if (full_current_host != CN)
  380.             Free(full_current_host);
  381.         full_current_host = NewString(n);
  382.     }
  383.     if (current_host != CN)
  384.         Free(current_host);
  385.     if (localprocess_cmd != CN) {
  386.         if (full_current_host[strlen(OptLocalProcess)] != '\0')
  387.         current_host = NewString(full_current_host +
  388.             strlen(OptLocalProcess) + 1);
  389.         else
  390.             current_host = NewString("default shell");
  391.     } else {
  392.         current_host = s;
  393.     }
  394.  
  395.     /* Attempt contact. */
  396.     ever_3270 = False;
  397.     net_sock = net_connect(chost, port, localprocess_cmd != CN, &pending);
  398.     if (net_sock < 0) {
  399. #if defined(X3270_DISPLAY) /*[*/
  400.         if (appres.once) {
  401.             /* Exit when the error pop-up pops down. */
  402.             exiting = True;
  403.         }
  404.         else if (appres.reconnect) {
  405.             auto_reconnect_inprogress = True;
  406.             (void) AddTimeOut(RECONNECT_ERR_MS, try_reconnect);
  407.         }
  408. #endif /*]*/
  409.         /* Redundantly signal a disconnect. */
  410.         st_changed(ST_CONNECT, False);
  411.         return -1;
  412.     }
  413.  
  414.     /* Success. */
  415.  
  416.     /* Set pending string. */
  417.     if (ps != CN)
  418.         login_macro(ps);
  419.  
  420.     /* Prepare Xt for I/O. */
  421.     x_add_input(net_sock);
  422.  
  423.     /* Set state and tell the world. */
  424.     if (pending) {
  425.         cstate = PENDING;
  426.         st_changed(ST_HALF_CONNECT, True);
  427.     } else {
  428.         cstate = CONNECTED_INITIAL;
  429.         st_changed(ST_CONNECT, True);
  430. #if defined(X3270_DISPLAY) /*[*/
  431.         if (appres.reconnect && error_popup_visible())
  432.             popdown_an_error();
  433. #endif /*]*/
  434.     }
  435.  
  436.     return 0;
  437. }
  438.  
  439. #if defined(X3270_DISPLAY) /*[*/
  440. /*
  441.  * Reconnect to the last host.
  442.  */
  443. static void
  444. host_reconnect(void)
  445. {
  446.     if (auto_reconnect_inprogress || current_host == CN ||
  447.         CONNECTED || HALF_CONNECTED)
  448.         return;
  449.     if (host_connect(reconnect_host) >= 0)
  450.         auto_reconnect_inprogress = False;
  451. }
  452.  
  453. /*
  454.  * Called from timer to attempt an automatic reconnection.
  455.  */
  456. static void
  457. try_reconnect(void)
  458. {
  459.     auto_reconnect_inprogress = False;
  460.     host_reconnect();
  461. }
  462. #endif /*]*/
  463.  
  464. void
  465. host_disconnect(Boolean failed)
  466. {
  467.     if (CONNECTED || HALF_CONNECTED) {
  468.         x_remove_input();
  469.         net_disconnect();
  470.         net_sock = -1;
  471. #if defined(X3270_DISPLAY) /*[*/
  472.         if (appres.once) {
  473.             if (error_popup_visible()) {
  474.                 /*
  475.                  * If there is an error pop-up, exit when it
  476.                  * pops down.
  477.                  */
  478.                 exiting = True;
  479.             } else {
  480.                 /* Exit now. */
  481.                 x3270_exit(0);
  482.                 return;
  483.             }
  484.         } else if (appres.reconnect && !auto_reconnect_inprogress) {
  485.             /* Schedule an automatic reconnection. */
  486.             auto_reconnect_inprogress = True;
  487.             (void) AddTimeOut(failed? RECONNECT_ERR_MS:
  488.                            RECONNECT_MS,
  489.                       try_reconnect);
  490.         }
  491. #endif /*]*/
  492.  
  493.         /*
  494.          * Remember a disconnect from ANSI mode, to keep screen tracing
  495.          * in sync.
  496.          */
  497. #if defined(X3270_TRACE) /*[*/
  498.         if (IN_ANSI && toggled(SCREEN_TRACE))
  499.             trace_ansi_disc();
  500. #endif /*]*/
  501.  
  502.         cstate = NOT_CONNECTED;
  503.  
  504.         /* Propagate the news to everyone else. */
  505.         st_changed(ST_CONNECT, False);
  506.     }
  507. }
  508.  
  509. /* The host has entered 3270 or ANSI mode, or switched between them. */
  510. void
  511. host_in3270(enum cstate new_cstate)
  512. {
  513.     Boolean now3270 = (new_cstate == CONNECTED_3270 ||
  514.                new_cstate == CONNECTED_SSCP ||
  515.                new_cstate == CONNECTED_TN3270E);
  516.  
  517.     cstate = new_cstate;
  518.     ever_3270 = now3270;
  519.     st_changed(ST_3270_MODE, now3270);
  520. }
  521.  
  522. void
  523. host_connected(void)
  524. {
  525.     cstate = CONNECTED_INITIAL;
  526.     st_changed(ST_CONNECT, True);
  527.  
  528. #if defined(X3270_DISPLAY) /*[*/
  529.     if (appres.reconnect && error_popup_visible())
  530.         popdown_an_error();
  531. #endif /*]*/
  532. }
  533.  
  534. /* Comparison function for the qsort. */
  535. static int
  536. host_compare(const void *e1, const void *e2)
  537. {
  538.     const struct host *h1 = *(const struct host **)e1;
  539.     const struct host *h2 = *(const struct host **)e2;
  540.     int r;
  541.  
  542.     if (h1->connect_time > h2->connect_time)
  543.         r = -1;
  544.     else if (h1->connect_time < h2->connect_time)
  545.         r = 1;
  546.     else
  547.         r = 0;
  548. #if defined(CFDEBUG) /*[*/
  549.     printf("%s %ld %d %s %ld\n",
  550.         h1->name, h1->connect_time,
  551.         r,
  552.         h2->name, h2->connect_time);
  553. #endif /*]*/
  554.     return r;
  555. }
  556.  
  557. #if defined(CFDEBUG) /*[*/
  558. static void
  559. dump_array(const char *when, struct host **array, int nh)
  560. {
  561.     int i;
  562.  
  563.     printf("%s\n", when);
  564.     for (i = 0; i < nh; i++) {
  565.         printf(" %15s %ld\n", array[i]->name, array[i]->connect_time);
  566.     }
  567. }
  568. #endif /*]*/
  569.  
  570. /* Save the most recent host in the recent host list. */
  571. static void
  572. save_recent(const char *hn)
  573. {
  574.     char *lcf_name = CN;
  575.     FILE *lcf = (FILE *)NULL;
  576.     struct host *h;
  577.     struct host *rest = (struct host *)NULL;
  578.     int n_ent = 0;
  579.     struct host *h_array[(MAX_RECENT * 2) + 1];
  580.     int nh = 0;
  581.     int i, j;
  582.     time_t t = time((time_t *)NULL);
  583.  
  584.     /* Allocate a new entry. */
  585.     if (hn != CN) {
  586.         h = (struct host *)Malloc(sizeof(*h));
  587.         h->name = NewString(hn);
  588.         h->hostname = NewString(hn);
  589.         h->entry_type = RECENT;
  590.         h->loginstring = CN;
  591.         h->connect_time = t;
  592.         h_array[nh++] = h;
  593.     }
  594.  
  595.     /* Put the existing entries into the array. */
  596.     for (h = hosts; h != (struct host *)NULL; h = h->next) {
  597.         if (h->entry_type != RECENT)
  598.             break;
  599.         h_array[nh++] = h;
  600.     }
  601.  
  602.     /* Save the ibm_hosts entries for later. */
  603.     rest = h;
  604.     if (rest != (struct host *)NULL)
  605.         rest->prev = (struct host *)NULL;
  606.  
  607.     /*
  608.      * Read the last-connection file, to capture the any changes made by
  609.      * other instances of x3270.  
  610.      */
  611.     if (appres.connectfile_name != CN &&
  612.         strcasecmp(appres.connectfile_name, "none")) {
  613.         lcf_name = do_subst(appres.connectfile_name, True, True);
  614.         lcf = fopen(lcf_name, "r");
  615.     }
  616.     if (lcf != (FILE *)NULL) {
  617.         char buf[1024];
  618.  
  619.         while (fgets(buf, sizeof(buf), lcf) != CN) {
  620.             int sl;
  621.             time_t connect_time;
  622.             char *ptr;
  623.  
  624.             /* Pick apart the entry. */
  625.             sl = strlen(buf);
  626.             if (buf[sl - 1] == '\n')
  627.                 buf[sl-- - 1] = '\0';
  628.             if (!sl ||
  629.                 buf[0] == '#' ||
  630.                 (connect_time = strtoul(buf, &ptr, 10)) == 0L ||
  631.                 ptr == buf ||
  632.                 *ptr != ' ' ||
  633.                 !*(ptr + 1))
  634.                 continue;
  635.  
  636.             h = (struct host *)Malloc(sizeof(*h));
  637.             h->name = NewString(ptr + 1);
  638.             h->hostname = NewString(ptr + 1);
  639.             h->entry_type = RECENT;
  640.             h->loginstring = CN;
  641.             h->connect_time = connect_time;
  642.             h_array[nh++] = h;
  643.             if (nh > (MAX_RECENT * 2) + 1)
  644.                 break;
  645.         }
  646.         fclose(lcf);
  647.     }
  648.  
  649.     /* Sort the array, in reverse order by connect time. */
  650. #if defined(CFDEBUG) /*[*/
  651.     dump_array("before", h_array, nh);
  652. #endif /*]*/
  653.     qsort(h_array, nh, sizeof(struct host *), host_compare);
  654. #if defined(CFDEBUG) /*[*/
  655.     dump_array("after", h_array, nh);
  656. #endif /*]*/
  657.  
  658.     /*
  659.      * Filter out duplicate host names, and limit the array to
  660.      * MAX_RECENT entries total.
  661.      */
  662.     hosts = (struct host *)NULL;
  663.     last_host = (struct host *)NULL;
  664.     for (i = 0; i < nh; i++) {
  665.         h = h_array[i];
  666.         if (h == (struct host *)NULL)
  667.             continue;
  668.         h->next = (struct host *)NULL;
  669.         if (last_host != (struct host *)NULL)
  670.             last_host->next = h;
  671.         h->prev = last_host;
  672.         last_host = h;
  673.         if (hosts == (struct host *)NULL)
  674.             hosts = h;
  675.         n_ent++;
  676.  
  677.         /* Zap the duplicates. */
  678.         for (j = i+1; j < nh; j++) {
  679.             if (h_array[j] &&
  680.                 (n_ent >= MAX_RECENT ||
  681.                  !strcmp(h_array[i]->name, h_array[j]->name))) {
  682. #if defined(CFDEBUG) /*[*/
  683.                 printf("%s is a dup of %s\n",
  684.                     h_array[j]->name, h_array[i]->name);
  685. #endif /*]*/
  686.                 Free(h_array[j]->name);
  687.                 Free(h_array[j]->hostname);
  688.                 Free(h_array[j]);
  689.                 h_array[j] = (struct host *)NULL;
  690.             }
  691.         }
  692.     }
  693.  
  694.     /* Re-attach the ibm_hosts entries to the end. */
  695.     if (rest != (struct host *)NULL) {
  696.         if (last_host != (struct host *)NULL) {
  697.             last_host->next = rest;
  698.         } else {
  699.             hosts = rest;
  700.         }
  701.         rest->prev = last_host;
  702.     }
  703.  
  704.     /* If there's been a change, rewrite the file. */
  705.     if (hn != CN &&
  706.         appres.connectfile_name != CN &&
  707.         strcasecmp(appres.connectfile_name, "none")) {
  708.         lcf = fopen(lcf_name, "w");
  709.         if (lcf != (FILE *)NULL) {
  710.             fprintf(lcf, "# Created %s# by %s\n", ctime(&t), build);
  711.             for (h = hosts; h != (struct host *)NULL; h = h->next) {
  712.                 if (h->entry_type != RECENT)
  713.                     break;
  714.                 (void) fprintf(lcf, "%lu %s\n", h->connect_time,
  715.                     h->name);
  716.             }
  717.         }
  718.         fclose(lcf);
  719.         Free(lcf_name);
  720.     }
  721. }
  722.  
  723. /* Support for state change callbacks. */
  724.  
  725. struct st_callback {
  726.     struct st_callback *next;
  727.     void (*func)(Boolean);
  728. };
  729. static struct st_callback *st_callbacks[N_ST];
  730. static struct st_callback *st_last[N_ST];
  731.  
  732. /* Register a function interested in a state change. */
  733. void
  734. register_schange(int tx, void (*func)(Boolean))
  735. {
  736.     struct st_callback *st;
  737.  
  738.     st = (struct st_callback *)Malloc(sizeof(*st));
  739.     st->func = func;
  740.     st->next = (struct st_callback *)NULL;
  741.     if (st_last[tx] != (struct st_callback *)NULL)
  742.         st_last[tx]->next = st;
  743.     else
  744.         st_callbacks[tx] = st;
  745.     st_last[tx] = st;
  746. }
  747.  
  748. /* Signal a state change. */
  749. void
  750. st_changed(int tx, Boolean mode)
  751. {
  752.     struct st_callback *st;
  753.  
  754.     for (st = st_callbacks[tx];
  755.          st != (struct st_callback *)NULL;
  756.          st = st->next) {
  757.         (*st->func)(mode);
  758.     }
  759. }
  760.  
  761. /* Explicit connect/disconnect actions. */
  762.  
  763. void
  764. Connect_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
  765. {
  766.     action_debug(Connect_action, event, params, num_params);
  767.     if (check_usage(Connect_action, *num_params, 1, 1) < 0)
  768.         return;
  769.     if (CONNECTED || HALF_CONNECTED) {
  770.         popup_an_error("Already connected");
  771.         return;
  772.     }
  773.     (void) host_connect(params[0]);
  774.  
  775.     /*
  776.      * If called from a script and the connection was successful (or
  777.      * half-successful), pause the script until we are connected and
  778.      * we have identified the host type.
  779.      */
  780.     if (!w && (CONNECTED || HALF_CONNECTED))
  781.         sms_connect_wait();
  782. }
  783.  
  784. #if defined(X3270_MENUS) /*[*/
  785. void
  786. Reconnect_action(Widget w, XEvent *event, String *params, Cardinal *num_params)
  787. {
  788.     action_debug(Reconnect_action, event, params, num_params);
  789.     if (check_usage(Reconnect_action, *num_params, 0, 0) < 0)
  790.         return;
  791.     if (CONNECTED || HALF_CONNECTED) {
  792.         popup_an_error("Already connected");
  793.         return;
  794.     }
  795.     if (current_host == CN) {
  796.         popup_an_error("No previous host to connect to");
  797.         return;
  798.     }
  799.     host_reconnect();
  800.  
  801.     /*
  802.      * If called from a script and the connection was successful (or
  803.      * half-successful), pause the script until we are connected and
  804.      * we have identified the host type.
  805.      */
  806.     if (!w && (CONNECTED || HALF_CONNECTED))
  807.         sms_connect_wait();
  808. }
  809. #endif /*]*/
  810.  
  811. void
  812. Disconnect_action(Widget w unused, XEvent *event, String *params,
  813.     Cardinal *num_params)
  814. {
  815.     action_debug(Disconnect_action, event, params, num_params);
  816.     if (check_usage(Disconnect_action, *num_params, 0, 0) < 0)
  817.         return;
  818.     host_disconnect(False);
  819. }
  820.